#include "model/datacenter.h"
#include "messageshowarea.h"
#include "mainwidget.h"
#include "toast.h"
#include "userinfowidget.h"
#include "soundrecorder.h"
#include <QScrollBar>
#include <QMenu>
#include <QTimer>
#include <QFileInfo>
#include <QFontMetrics>
#include <QFileDialog>
#include <QVBoxLayout>
#include <QPushButton>
#include <QLabel>
#include <QPainter>
#include <QPainterPath>
#include <QGridLayout>
#include <QStyleOption>
#include <QPainter>
#include "debug.h"

using namespace model;
MessageShowArea::MessageShowArea(QWidget *parent)
    : QScrollArea(parent)
{
    //1.初始化基本属性
    this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    this->setWidgetResizable(true);
    this->verticalScrollBar()->setStyleSheet("QScrollBar:vertical { width: 2px; background-color: rgb(240, 240, 240);}");
    this->horizontalScrollBar()->setStyleSheet("QScrollBar:horizontal { height: 0px; }");
    this->setStyleSheet("QScrollArea {border: none;}");

    //2.创建container 把东西塞进去
    container = new QWidget();
    this->setWidget(container); //QScrollArea 必须 再set

    //3. 给container添加竖直管理器
    QVBoxLayout* layout = new QVBoxLayout();
    layout->setContentsMargins(0, 5, 0, 0);
    layout->setSpacing(0);
    layout->setAlignment(Qt::AlignTop);
    container->setLayout(layout);

    //向上设置间距

    //添加 构造测试数据 逻辑
#if TEST_UI

    for(int i = 0; i < 30; i ++)
    {
        UserInfo userInfo;
        userInfo.userID = QString::number(i);
        userInfo.phone = "15| test " + QString::number(i);
        userInfo.nickname = "xiang" + QString::number(i);
        userInfo.description = "test" + QString::number(i);
        userInfo.avatar = QIcon(":/resource/image/defaultAvatar.png");

        Message message = Message::makeMessage(TEXT_TYPE, "", userInfo,(QString("这是一条测试消息") + QString::number(i)).toUtf8(), "");

        if(i % 2 == 0)
        this->addMessage(true, message);
        else if(i % 3 == 0)
        this->addMessage(false, message);
        else
        {
            QString test = (QString("这是一条很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长很长的测试消息") + QString::number(i));
            Message message = Message::makeMessage(TEXT_TYPE, "", userInfo, test.toUtf8(), "");
            this->addMessage(false, message);
        }


    }

#endif
}

void MessageShowArea::addMessage(bool isLeft, const Message &message)
{
    //构造MessageItem 添加到布局管理器
    LOG() << "MessageShowArea::addMessage -> lassMessage.chatSessionID" << message.chatSessionID;

    MessageItem* messageItem = MessageItem::makeMessageItem(isLeft, message);
    if(!messageItem)
    {
        LOG() << "erro messageItem is null";
    }
    qDebug() << "messageItem 尺寸：" << messageItem->size(); // 正常应 > (0,0
    //LOG() << "message content " << QString(message.content);
    container->layout()->addWidget(messageItem);

}

void MessageShowArea::addFontMessage(bool isLeft, const Message &message)
{
    MessageItem* messageItem = MessageItem::makeMessageItem(isLeft, message);
    QVBoxLayout* layout = dynamic_cast<QVBoxLayout*>(container->layout());
    //LOG() << "message content " << QString(message.content);
    layout->insertWidget(0, messageItem);
}

void MessageShowArea::clear()
{
    //遍历 删除
    QLayout* layout = container->layout();
    //遍历清空
    for(int i = layout->count() - 1; i >= 0; i--)
    {
        QLayoutItem* item = layout->takeAt(i);
        // 释放
        if(item && item->widget())
            delete item->widget();
    }
}

void MessageShowArea::scrollToEnd()
{
    // 实现思路:
    // 拿到滚动区域中的滚动条(垂直滚动条)
    // 获取到滚动条的最大值
    // 根据最大值, 设置滚动条的滚动位置.
    // 为了使滚动效果更佳, 能够在界面绘制好之后进行滚动条的设置
    // 给这里的滚动操作, 加上个 "延时"

    QTimer* timer = new QTimer();
    connect(timer, &QTimer::timeout, this, [=]() {
        // 获取到垂直滚动条的最大值
        int maxValue = this->verticalScrollBar()->maximum();
        // 设置滚动条的滚动位置
        this->verticalScrollBar()->setValue(maxValue);

        timer->stop();
        timer->deleteLater();
    });
    timer->start(500);
}

MessageItem::MessageItem(bool isLeft)
    :isLeft(isLeft)
{}

MessageItem* MessageItem::makeMessageItem(bool isLeft, const Message &message)
{
    //1.创建对象 和布局管理器
    MessageItem* messageItem = new MessageItem(isLeft);
    QGridLayout* layout = new QGridLayout();
    layout->setContentsMargins(30,0,40,0);
    layout->setSpacing(10);
    messageItem->setLayout(layout);
    //最低不能低于100
    messageItem->setMinimumHeight(100);
    //2. 创建头像
    LOG() << "MessageItem::makeMessageItem -> lassMessage.chatSessionID" << message.chatSessionID;
    QPushButton* avatarBtn = new QPushButton();
    avatarBtn->setFixedSize(40, 40);
    avatarBtn->setIconSize(QSize(40, 40));
    avatarBtn->setIcon(message.sender.avatar);
    avatarBtn->setStyleSheet("QPushButton { border: none; background-color: transparent; }");
    if(isLeft)
    {
        layout->addWidget(avatarBtn, 0, 0, 2, 1, Qt::AlignTop | Qt::AlignLeft);
    }
    else
    {
        layout->addWidget(avatarBtn, 0, 1, 2, 1, Qt::AlignTop | Qt::AlignLeft);
    }
    //3. 创建名称和时间
    QLabel* namelabel = new QLabel();
    namelabel->setText(message.sender.nickname + " | " + message.time);
    namelabel->setAlignment(Qt::AlignBottom);
    namelabel->setStyleSheet("QLabel { font-size: 12px; color: rgb(178, 178, 178); }");
    if(isLeft)
    {
        layout->addWidget(namelabel, 0, 1, Qt::AlignLeft);
    }
    else
    {
        layout->addWidget(namelabel, 0, 0, Qt::AlignRight);
    }
    //4. 创建消息体
    QWidget* contentWidget = nullptr;
    switch(message.messageType)
    {
        case model::TEXT_TYPE:
            contentWidget = makeTextMessageItem(isLeft, message.content);
            break;
        case model::IMAGE_TYPE:
            contentWidget = makeImageMessageItem(message.fileID, message.content, isLeft);
            break;
        case model::FILE_TYPE:
            contentWidget = makeFileMessageItem(isLeft, message);
            break;
        case model::SPEECH_TYPE:
            contentWidget = makeSpeechMessageItem(isLeft, message);
            break;
        default:
            LOG() << "error message type : " << message.messageType;
    }
    if(isLeft)
    {
        layout->addWidget(contentWidget, 1, 1);
    }
    else
    {
        layout->addWidget(contentWidget, 1, 0);
    }

    //5.链接信号槽 处理用户点击头像的操作
    connect(avatarBtn, &QPushButton::clicked, messageItem, [=](){
        MainWidget* mainWidget = MainWidget::getInstance();
        UserInfoWidget* userInfoWidget = new UserInfoWidget(message.sender, mainWidget);
        userInfoWidget->exec();
    });

    //当用户修改昵称 会话展示区同步修改所有该用户的昵称
    if(!isLeft)
    {
        DataCenter* dataCenter = DataCenter::getInstance();
        connect(dataCenter, &DataCenter::changeNicknameDone, messageItem, [=](){
            namelabel->setText(dataCenter->getMyself()->nickname + " | " + message.time);
        });
    }
    //当用户修改头像 会话展示区同步修改所有该用户的头像
    if(!isLeft)
    {
        DataCenter* dataCenter = DataCenter::getInstance();
        connect(dataCenter, &DataCenter::changeAvatarDone, messageItem, [=](){
            avatarBtn->setIcon(dataCenter->getMyself()->avatar);
        });
    }


    return messageItem;
}

QWidget* MessageItem::makeTextMessageItem(bool isLeft, const QString& text)
{
    MessageContentLabel* messageContentLabel = new MessageContentLabel(text, isLeft);

    return messageContentLabel;
}

QWidget* MessageItem::makeImageMessageItem(const QString& fileId, const QByteArray& imageData, bool isLeft)
{
    MessageImageLabel* messageImageLabel = new MessageImageLabel(fileId, imageData, isLeft);
    return messageImageLabel;
}


QWidget *MessageItem::makeFileMessageItem(bool isLeft, const Message& message)
{
    MessageContentLabel* messageContentLabel = new MessageContentLabel("[文件] " + message.fileName, isLeft, message.messageType,
                                                                       message.fileID, message.content, message.fileName);
    return messageContentLabel;
}

QWidget *MessageItem::makeSpeechMessageItem(bool isLeft, const Message& message)
{
    MessageContentLabel* messageContentLabel = new MessageContentLabel("[语音消息]", isLeft, message.messageType,
                                                                       message.fileID, message.content);
    return messageContentLabel;
}
//////////////////////////////////////////////
/// 创建类表示 文本消息 文件消息
//////////////////////////////////////////////
MessageContentLabel::MessageContentLabel(const QString& text, bool isLeft, const model::Message_Type& messageType,
                        const QString& fileId, const QByteArray& fileData, const QString& fileName)
    :isLeft(isLeft)
    ,messageTyoe(messageType)
    ,fileId(fileId)
    ,fileData(fileData)
    ,fileName(fileName)
{
    this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    QFont font;
    font.setFamily("微软雅黑");
    font.setPixelSize(16);


    this->label = new QLabel(this);
    this->label->setText(text);
    this->label->setFont(font);
    this->label->setAlignment(Qt::AlignVCenter | Qt::AlignLeft);
    this->label->setWordWrap(true); //自动换行
    this->label->setStyleSheet("QLabel { padding: 0 10px; line-height: 1.2; background-color: transparent;}");//行间距 = 16 * 1.2 - 16 = 3.2
    //LOG() << label->text();

    //针对文件消息 并且content 为空的情况下 通过网络加载文件内容
    if(messageType == model::TEXT_TYPE)
    {
        //文本消息 不需要额外处理
        return;
    }

    if(fileData.isEmpty())
    {
        DataCenter* dataCenter = DataCenter::getInstance();
        connect(dataCenter, &DataCenter::getSingleFileDone, this, &MessageContentLabel::updateUI);
        dataCenter->getSingleFileAsync(fileId);
    }
    else
    {
        //content 不为空 已经有了 直接标记完成
        loadContentDone = true;
    }

}
//函数会在该控件被显示的时候自动调用 窗口大小调整 打开 关闭 发消息
void MessageContentLabel::paintEvent(QPaintEvent *event)
{
    (void) event;
    //1. 获取父元素宽度
    QObject* object = this->parent();
    if(!object->isWidgetType())
    {
        //不是预期的QWidget 此时不需要进行任何后续绘制
        return;
    }
    QWidget* parent = dynamic_cast<QWidget* >(object);
    int width = parent->width() * 0.6; //一行文字显示不超过主界面60% 包含左右边距

    //2. 计算当前文本 如果一行放置 需要多宽
    QFontMetrics metrics(this->label->font()); //初始化字体 再计算宽度
    int totalWidth = metrics.horizontalAdvance(this->label->text());
    //LOG() << totalWidth;

    //3.计算出行数
    int rows = (totalWidth / (width - 40)) + 1; //左右还有二十像素边距
    if (totalWidth + 40 < width) {
       width = totalWidth + 40;
    }
    //4. 根据行数 计算得到高度 上下边距10px
    int height = rows * (this->label->font().pixelSize() * 1.2) + 20; //总高度 = 行高(字体大小 * 1.2) + 20(上下边距)

    //5.绘制圆角矩形 和箭头
    QPainter painter(this);
    QPainterPath path; //绘制不规则形装
        //设置 抗锯齿
    painter.setRenderHint(QPainter::Antialiasing);
    if(isLeft)
    {
        //左侧用白色画笔画刷   画笔画轮廓 画刷画里面
        painter.setPen(QPen(QColor(255, 255, 255), 1));
        painter.setBrush((QColor(255, 255, 255)));
        //圆角矩形 相对父元素坐标 长 宽 x 方向的圆角半径 y 方向的圆角半径
        painter.drawRoundedRect(10, 0, width, height, 10, 10);
        path.moveTo(10, 15);
        path.lineTo(0, 20);
        path.lineTo(10, 25);
        path.closeSubpath(); //使绘制的线成闭合的多边形 方便填色
        painter.drawPath(path);
        this->label->setGeometry(10, 0, width, height);
    }
    else
    {
        //右侧用绿色画笔画刷
        painter.setPen(QPen(QColor(137, 217, 97), 1));
        painter.setBrush((QColor(137, 217, 97)));
        //圆角矩形 左侧 横坐标
        int leftPos = this->width()- width - 10;
        //圆角矩形 右侧 横坐标
        int rihgtPos = this->width() - 10;
        //绘制圆角矩形
        painter.drawRoundedRect(leftPos, 0, width, height, 10, 10);
        //绘制箭头
        path.moveTo(rihgtPos, 15);
        path.lineTo(rihgtPos + 10, 20);
        path.lineTo(rihgtPos, 25);
        path.closeSubpath(); //使绘制的线成闭合的多边形 方便填色
        painter.drawPath(path);
        this->label->setGeometry(leftPos, 0, width, height);
    }

    //6.重新设置父元素的高度 确保能容纳下
    parent->setFixedHeight(height + 50); //考虑到用户名和时间
    //qDebug() << "Label 几何信息：" << this->label->geometry();
    //qDebug() << "Label 父部件：" << this->label->parentWidget();
}

void MessageContentLabel::updateUI(const QString &fileId, const QByteArray &fileData)
{
    if(this->fileId != fileId)
    {
        //不是同一个文件 不处理
        return;
    }
    this->fileData = fileData;
    this->loadContentDone = true;

    //对于文件消息来说 要在界面上显示 "[文件] file.txt" 这种形式 和文件正文无关
    //从服务器拿到文件内容前 界面就渲染好了的 所以update没有也行


    //更新界面 触发绘制
    this->update();
}

void MessageContentLabel::mousePressEvent(QMouseEvent *event)
{
    //鼠标点击后下载文件
    (void) event;
    //判断是不是鼠标左键点击
    if(event->button() == Qt::LeftButton)
    {
        if(messageTyoe == model::FILE_TYPE)
        {
            if(!loadContentDone)
            {
                Toast::showMessage("文件尚未加载完成 请稍后重试");
                return;
            }
            //把fileData写道文件里
            saveFile();
        }
        else if(messageTyoe == model::SPEECH_TYPE)
        {
            if(!loadContentDone)
            {
                Toast::showMessage("语音尚未加载完成 请稍后重试");
                return;
            }
            SoundRecorder* soundRecorder = SoundRecorder::getInstance();
            connect(soundRecorder, &SoundRecorder::soundPlayStart, this, &MessageContentLabel::startPlay, Qt::UniqueConnection);
            connect(soundRecorder, &SoundRecorder::soundPlayDone, this, &MessageContentLabel::playDone , Qt::UniqueConnection);
            soundRecorder->startPlay(fileData);
            label->setText("正在播放...");
        }
    }

}

void MessageContentLabel::saveFile()
{
    //保存时默认添加文件名
    QString savePath;
    if(fileName.isEmpty() || fileName == "")
    {
        savePath = QFileDialog::getSaveFileName(this, "保存文件", QDir::homePath(), "所有文件 (*.*)");
        if(savePath.isEmpty())
        {
            //用户取消保存操作
            return;
        }
    }
    else
    {
        savePath = QFileDialog::getSaveFileName(this, "保存文件", QDir::homePath() + "/" + fileName, "所有文件 (*.*)");
        if(savePath.isEmpty())
        {
            //用户取消保存操作
            return;
        }
    }
    model::writeByteArrayToFile(savePath, fileData);
}

void MessageContentLabel::startPlay()
{
    if(label->text() == "正在播放...")
    {
        label->setText("[语音消息]");
    }
}

void MessageContentLabel::playDone()
{
    if(label->text() == "正在播放...")
    {
        label->setText("[语音消息]");
    }
}

void MessageContentLabel::contextMenuEvent(QContextMenuEvent *event)
{
    (void) event;
    if(messageTyoe != model::SPEECH_TYPE)
    {
        //非语音消息 不处理
        return;
    }
    LOG() << "MessageContentLabel::contextMenuEvent";

    QMenu* menu = new QMenu(this);
    menu->setStyleSheet(R"(
        QMenu {
            background-color: #222;
            color: #f2f2f2;
            border: 1px solid #555;
            border-radius: 8px;
            padding: 8px;
            font-size: 15px;
            min-width: 140px;
        }
        QMenu::item {
            background: transparent;
            padding: 8px 24px 8px 20px;
            margin: 2px 0;
            border-radius: 6px;
        }
        QMenu::item:selected {
            background: #44aaff;
            color: #fff;
        }
        QMenu::separator {
            height: 1px;
            background: #555;
            margin: 6px 0;
        }
    )");
    QAction* action = menu->addAction("语音转文字");
    connect(action, &QAction::triggered, this, [=]()
    {
        DataCenter* dataCenter = DataCenter::getInstance();
        connect(dataCenter, &DataCenter::speechToTextDone, this, &MessageContentLabel::speechToTextDone, Qt::UniqueConnection);
        dataCenter->speechToTextAsync(fileId, fileData);
    });

    //显示菜单及其选项
    menu->popup(event->globalPos()); //自动管理删除

}

void MessageContentLabel::speechToTextDone(const QString &fileId, const QString &text)
{
    if(this->fileId != fileId)
    {
        //不是同一个文件 不处理
        return;
    }
    //更新界面
    this->label->setText("[语音转文字] " + text);
    //重新绘制
    this->update();
}



/////////////////////////////////////////////////////////////////////////////////////////////////////////////
///约定客户端向服务器发起获取文件信息请求时(图片 文件 语音)时 服务器只返回文件id
/// 客户端根据文件id 再向服务器发起二次请求 获取文件内容
/////////////////////////////////////////////////////////////////////////////////////////////////////////////




//////////////////////////////////////////////
/// 创建类表示 图片消息
//////////////////////////////////////////////
MessageImageLabel::MessageImageLabel(const QString &fileId, const QByteArray &imageData, bool isLeft)
    :fileId(fileId)
    , imageData(imageData)
    , isLeft(isLeft)

{
    imageButton = new QPushButton(this);
    imageButton->setStyleSheet("QPushButton { border: none; background-color: transparent; }");
    this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);

    //两种情况
    //1. 自己发的图片 fileId = "" imageData 有数据
    //2. 别人发的图片 fileId 有数据 imageData = ""
    if(imageData.isEmpty())
    {
        //别人发的图片 拿fileId去获取图片数据
        LOG() << "MessageImageLabel::MessageImageLabel -> fileId " << fileId;
        DataCenter* dataCenter = DataCenter::getInstance();
        connect(dataCenter, &DataCenter::getSingleFileDone, this, &MessageImageLabel::updateUI);
        dataCenter->getSingleFileAsync(fileId);
    }
    // else if(fileId.isEmpty())
    // {
    //     //自己发的图片
    //     // LOG() << "MessageImageLabel::MessageImageLabel -> self send image ";
    //     // //直接更新界面
    //     // this->updateUI(fileId, imageData);
    // }
}

void MessageImageLabel::paintEvent(QPaintEvent *event)
{
    (void) event;
    // 1. 先拿到该元素的父元素, 看父元素的宽度是多少.
    //    此处显示的图片宽度的上限 父元素宽度的 60% .
    QObject* object = this->parent();
    if (!object->isWidgetType())
    {
        // 这个逻辑理论上来说是不会存在的.
        return;
    }
    QWidget* parent = dynamic_cast<QWidget*>(object);
    int width = parent->width() * 0.6;

    // 2. 加载二进制数据为图片对象
    QImage image;
    if (imageData.isEmpty())
    {
        // 此时图片的响应数据还没回来.
        // 此处先拿一个 "固定默认图片" 顶替一下.
        QByteArray tmpContent = loadFileToByteArray(":/resource/image/image.png");
        image.loadFromData(tmpContent);
    }
    else
    {
        // 此处的 load 操作 QImage 能够自动识别当前图片是啥类型的 (png, jpg....)
        image.loadFromData(imageData);
    }

    // 3. 针对图片进行缩放.
    int height = 0;
    if (image.width() > width)
    {
        // 发现图片更宽, 就需要把图片缩放一下, 使用 width 作为实际的宽度
        // 等比例缩放.
        height = ((double)image.height() / image.width()) * width;
    }
    else
    {
        // 图片本身不太宽, 不需要缩放.
        width = image.width();
        height = image.height();
    }

    // pixmap 只是一个中间变量. QImage 不能直接转成 QIcon, 需要 QPixmap 中转一下
    QPixmap pixmap = QPixmap::fromImage(image);
    // imageBtn->setFixedSize(width, height);
    imageButton->setIconSize(QSize(width, height));
    imageButton->setIcon(QIcon(pixmap));

    // 4. 由于图片高度是计算算出来的. 该元素的父对象的高度, 能够容纳下当前的元素.
    //    此处 + 50 是为了能够容纳下 上方的 "名字" 部分. 同时留下一点 冗余 空间.
    parent->setFixedHeight(height + 50);

    // 5. 确定按钮所在的位置.
    //    左侧消息, 和右侧消息, 要显示的位置是不同的.
    if (isLeft)
    {
        imageButton->setGeometry(10, 0, width, height);
    }
    else
    {
        int leftPos = this->width() - width - 10;
        imageButton->setGeometry(leftPos, 0, width, height);
    }
}

void MessageImageLabel::updateUI(const QString& fileId, const QByteArray& imageData)
{
    if(this->fileId != fileId)
    {
        //不是同一个图片 不处理
        return;
    }
    this->imageData = imageData;

    //更新界面 触发绘制
    //Q :this->update() 会在初始化的时候触发吗 A :会的
    this->update();
}
















